use crate::controller::Error;
use crate::controller::error::ObjectHasBeenReferenceError;
use crate::dto;
use crate::service::ServiceSet;
use std::sync::Arc;

pub struct NetworkAclController {
    service_set: Arc<ServiceSet>,
}

impl NetworkAclController {
    pub fn new(service_set: Arc<ServiceSet>) -> Self {
        Self { service_set }
    }
    pub async fn add(
        &self,
        request: &dto::network_acl::AddRequest,
    ) -> Result<dto::core::network_acl::AddResponseData, Error> {
        let data = self.service_set.network_acl_manager.add(&request).await?;

        Ok(data)
    }

    pub async fn list(
        &self,
        request: &dto::network_acl::ListRequest,
    ) -> Result<dto::network_acl::ListResponseData, Error> {
        let total_count = self
            .service_set
            .network_acl_manager
            .count(&request.filter)
            .await?;

        if total_count == 0 {
            return Ok(dto::network_acl::ListResponseData {
                total_count,
                list: Vec::new(),
            });
        }

        let list = self
            .service_set
            .network_acl_manager
            .list(&request.filter, request.offset, request.limit)
            .await?;

        Ok(dto::network_acl::ListResponseData { total_count, list })
    }

    pub async fn get(
        &self,
        request: &dto::network_acl::GetRequest,
    ) -> Result<dto::core::network_acl::NetworkAclItem, Error> {
        let data = self
            .service_set
            .network_acl_manager
            .get(request.network_acl_id.as_str())
            .await?;

        Ok(data)
    }

    pub async fn update(&self, request: &dto::network_acl::UpdateRequest) -> Result<(), Error> {
        if request.params == Default::default() {
            return Ok(());
        }
        
        self.service_set
            .network_acl_manager
            .update(request.network_acl_id.as_str(), &request.params)
            .await?;

        Ok(())
    }

    pub async fn delete(&self, request: &dto::network_acl::DeleteRequest) -> Result<(), Error> {
        self.check_has_been_referenced(request.network_acl_id.as_str())
            .await?;

        self.service_set
            .network_acl_manager
            .delete(request.network_acl_id.as_str())
            .await?;
        Ok(())
    }

    async fn check_has_been_referenced(&self, network_acl_id: &str) -> Result<(), Error> {
        if let Some(v) = self
            .service_set
            .traffic_steering_rule
            .list(
                &dto::core::traffic_steering_rule::Filter {
                    network_acl_id: Some(network_acl_id.to_string()),
                    ..Default::default()
                },
                0,
                1,
            )
            .await?
            .get(0)
        {
            return Err(ObjectHasBeenReferenceError::traffic_steering_rule(
                v.traffic_steering_rule_id.as_str(),
            )
            .into());
        }

        Ok(())
    }
}
